package com.xw.mailservice.task;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;

import com.xw.mailservice.beans.MailBox;
import com.xw.mailservice.contents.MailBoxStateEnums;
import com.xw.mailservice.dao.MailBoxDao;
import com.xw.mailservice.mail.model.MailSession;
import com.xw.mailservice.mail.receiver.FatchMailThread;
import com.xw.mailservice.mail.utils.AuthenticatorGenerator;
import com.xw.mailservice.model.MailServerModel;
import com.xw.mailservice.service.MailService;
import com.xw.mailservice.service.ServerStateService;
import com.xw.mailservice.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 获取邮件的定时任务 每间隔多少时间获取，邮件网关的集群逻辑
 * @author 杨建全
 */
@Component
public class GetMailTask {
	
	private static Logger logger = LoggerFactory.getLogger(GetMailTask.class);
	
	@Autowired
	private MailService mailService;
	@Autowired
	private MailBoxDao mailBoxDao;
	@Autowired
	private ServerStateService serverStateService;
	
	public static final int ServerDownTime = 60000; // 认为服务器挂掉的时长
	
	private static List<MailSession> sessionList = new ArrayList<>();
	private static List<FatchMailThread> mailGetThread = new ArrayList<FatchMailThread>();
	
	/**
	 * 描述：更新缓存中的存活时间
	 * @author 杨建全
	 * @date 2017年6月12日
	 */
	@Scheduled(fixedDelay=100,initialDelay=10*1000)
	public void updateCheckTime(){
		MailServerModel msm = serverStateService.getNowFatchingServer();
		MailServerModel mySelf = serverStateService.getServerModel();
		MailServerModel mailServerModel = serverStateService.getClusterServerInfoByName(mySelf.ipAndName());
		//正常更新
		if (null!=mailServerModel) {
			if (null != msm && msm.equals(mySelf)) {
				mailServerModel.setInFatching(true);
			}
			mailServerModel.setCheckTime(new Date());
			serverStateService.updateClusterServerInfoByName(mailServerModel.ipAndName(), mailServerModel);
		}else{
			//缓存丢失了,重新加入
			mySelf.setCheckTime(new Date());
			mySelf.setStartTime(new Date());
			if (null != msm && msm.equals(mySelf)) {
				mySelf.setInFatching(true);
			}
			serverStateService.updateClusterServerInfoByName(mySelf.ipAndName(), mySelf);
		}
		//更新当前拉取服务器时间搓（如果是自己的话）
		if (null == msm) {
			//当前没有拉取服务器的缓存了！随便设置一个不存在的
			MailServerModel temp = new MailServerModel();
			temp.setCheckTime(new Date());
			temp.setIp("127.0.0.1");
			temp.setName("CACHE_ERROR");
			temp.setStartTime(new Date());
			serverStateService.updateNowFatchingServerInfo(temp);
		}else if(msm.equals(mySelf)) {
			msm.setCheckTime(new Date());
			serverStateService.updateNowFatchingServerInfo(msm);
		}
	}
	
	/**
	 * 描述：监控集群状态，判断是否需要启动邮件收取任务
	 * @author 杨建全
	 * @date 2017年6月12日
	 */
	@Scheduled(fixedRate=500)
	public void monitorServerClusterState(){
		//检查服务器更新的时间，如果1分钟都没更新，那么认为服务器已经挂了
		//这个时候从服务器列表中选出除了挂掉的服务器的最早启动的服务器当成邮件收取服务器
		MailServerModel mailServerModel = serverStateService.getNowFatchingServer();
		if(null!=mailServerModel && mailServerModel.getCheckTime()!=null) {
			Date checkTime = mailServerModel.getCheckTime();
			Date now = new Date();
			long def = DateUtil.defTime(now, checkTime);
			if (def> ServerDownTime) {
				//认为服务器挂机了,移除当前服务器的key，判断自己是不是需要启动
				serverStateService.deleteClusterServerInfoByName(mailServerModel.ipAndName());
				//判断是否是自己需要启动
				Map<String, MailServerModel> clusters = serverStateService.getClusterServers();
				MailServerModel mySelf = serverStateService.getServerModel();
				//获取正常的服务器集合
				List<MailServerModel> msmList = new ArrayList<>(clusters.values());
				if (!msmList.isEmpty()) {
					//根据启动时间排序，选出最先启动的当成收取邮件服务器
					Collections.sort(msmList);
					MailServerModel lastestStartServer = msmList.get(0);
					//判断是不是自己，是自己就启动
					if (lastestStartServer.equals(mySelf)) {
						//启动邮件获取任务
						start();
						//更新相关key
						lastestStartServer.setInFatching(true);
						serverStateService.updateNowFatchingServerInfo(lastestStartServer);
					}
				}
			}
		}
		//判断集群中其他机器状态，移除异常的
		Map<String, MailServerModel> clusterServers = serverStateService.getClusterServers();
		for (Entry<String, MailServerModel> serverEntry : clusterServers.entrySet()) {
			MailServerModel msm = serverEntry.getValue();
			if(null!=msm && msm.getCheckTime()!=null) {
				Date checkTime = msm.getCheckTime();
				Date now = new Date();
				long def = DateUtil.defTime(now, checkTime);
				if (def> ServerDownTime) {
					//服务器集群失效了，移除
					serverStateService.deleteClusterServerInfoByName(msm.ipAndName());
				}
			}
		}
	}
	
	/**
	 * 描述：集群环境下启动的时候判断是否需要开启收取邮件线程
	 * @author 杨建全
	 * @date 2017年6月12日
	 * @return
	 */
	public boolean checkIsNeedToFatchMailWhenStart(){
		//启动的时候首先判断缓存中有没有我，如果1没有，就设置进去，否者2就不处理
		//设置进去之后，判断当前是否有机器在拉取邮件，如果没有，开启邮件拉取线程，否者不管
		MailServerModel mySelf = serverStateService.getServerModel();
		MailServerModel mailServerModel = serverStateService.getClusterServerInfoByName(mySelf.ipAndName());
		//设置进缓存中
		if (null==mailServerModel) {
			mySelf.setStartTime(new Date());
			mySelf.setCheckTime(new Date());
			serverStateService.updateClusterServerInfoByName(mySelf.ipAndName(), mySelf);
		}
		//判断是否需要开启收取任务
		MailServerModel msm = serverStateService.getNowFatchingServer();
		//没有服务器收取邮件,如果是自己，也要开启，可能是缓存残留
		if (msm==null || mySelf.equals(msm)) {
			mySelf.setInFatching(true);
			serverStateService.updateNowFatchingServerInfo(mySelf);
			return true;
		}
		return false;
	}
	
	private void init() throws NoSuchProviderException{
		//读取配置
		List<MailBox> mailBoxList = mailBoxDao.getAllMailBox();
		//循环邮箱
		for (MailBox mailBox : mailBoxList) {
			if (mailBox.getIsEnable()== MailBoxStateEnums.CLOSED) {
				//过滤掉关闭的邮箱
				continue;
			}
			//获取用户名密码
			String userName = mailBox.getMailBoxName();
			String password = mailBox.getPassword();
			//获取配置项
			//List<MailBoxConfig> configList = mailBox.getConfigList();
			Properties prop = new Properties();
			
			prop.put("mail.from", userName);
			prop.put("mail.store.protocol", "imap");
			//prop.put("mail.smtp.host", mailBox.getSmtpUrl());
			//prop.put("mail.smtp.port", mailBox.getSmtpPort());
			//prop.put("mail.smtp.auth", "true");
			prop.put("mail.imap.host", mailBox.getImapUrl());
			prop.put("mail.imap.port", mailBox.getImapPort());
			prop.put("mail.imap.ssl.enable", "true");
			//prop.put("mail.imap.auth.login.disable", "false");
			
			/*for (MailBoxConfig mailBoxConfig : configList) {
				prop.put(mailBoxConfig.getConfigName(), mailBoxConfig.getConfigValue());
			}*/
			Session session = Session.getInstance(prop, AuthenticatorGenerator.getAuthenticator(userName,password));
			Store store = session.getStore();
			//保存起来
			session.setDebug(true);
			MailSession ms = new MailSession();
			ms.setSession(session);
			ms.setStore(store);
			ms.setMailBoxName(userName);
			GetMailTask.sessionList.add(ms);
		}
	}
	
	/**
	 * 描述：启动邮件获取任务
	 * @author 杨建全
	 * @throws NoSuchProviderException 
	 * @throws MessagingException
	 * @date 2017年4月19日
	 */
	public void start(){
		try {
			init();
		} catch (NoSuchProviderException e) {
			logger.error("邮件收取任务初始化失败！！！,异常原因：",e);
			return;
		}
		for (final MailSession ms : GetMailTask.sessionList) {
			FatchMailThread fatchMailThread= new FatchMailThread(ms.getStore(), mailService,ms.getMailBoxName());
			//启动线程获取邮件
			fatchMailThread.start();
			//将当前线程加入到集合中，便于监控
			mailGetThread.add(fatchMailThread);
			logger.info("【"+ms.getMailBoxName()+"】邮箱收取任务已启动！");
		}
	}
	
	/**
	 * 描述：重新启动邮件抓取任务
	 * @author 杨建全
	 * @date 2017年11月14日
	 */
	public void stop(){
		for (FatchMailThread fatchMailThread : mailGetThread) {
			FatchMailThread.KeepIdleAliveThread keepAliveThread = fatchMailThread.getKeepAliveThread();
			keepAliveThread.interrupt();
			fatchMailThread.interrupt();
			logger.info("邮箱："+fatchMailThread.getMailBoxName()+"收取任务已停止！");
		}
		mailGetThread.clear();
		sessionList.clear();
	}
	
	/**
	 * 描述：重启邮件抓取任务
	 * @author 杨建全
	 * @date 2017年11月14日
	 */
	public void restart(){
		logger.info("准备重启邮件抓取任务...");
		this.stop();
		this.start();
	}

	public static List<FatchMailThread> getMailGetThread() {
		return mailGetThread;
	}

	public static void setMailGetThread(List<FatchMailThread> mailGetThread) {
		GetMailTask.mailGetThread = mailGetThread;
	}

}
